package com.gjy.redis.aspect;

import com.gjy.redis.annotation.RedisLock;
import com.gjy.redis.domain.AjaxResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author gjy
 * @version 1.0
 * @since 2025-06-27 09:13:39
 */
@Aspect
@Component
public class RedisLockAspect {

    private static final Logger log = LoggerFactory.getLogger(RedisLockAspect.class);
    @Resource
    private Jedis jedis;
    @Resource
    private Redisson redisson1;
    /**
     * 获取方法参数定义名
     */
    private final DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
    /**
     * 用于 SPEL 表达式解析
     */
    private final SpelExpressionParser parser = new SpelExpressionParser();

    @Around("@annotation(redisLock)")
    public Object aroundRedisLock(ProceedingJoinPoint point, RedisLock redisLock) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        String redisKey = getValBySpEl(redisLock.args(), signature, point.getArgs());
        String value = getValBySpEl(redisLock.value(), signature, point.getArgs());
        String prefix = redisLock.prefix().getValue();
        long timeout = redisLock.timeout();

        String r = prefix + "_" + redisKey;
        RLock lock = redisson1.getLock(r);
        boolean locked = false;
        try {
            locked = lock.tryLock(0, timeout, TimeUnit.SECONDS);
            if (!locked) {
                log.error("加锁失败: {},{}", r, value);
                return AjaxResult.builder().code(200).message("加锁失败: " + r).build();
            }
            Object proceed = point.proceed();
            log.info("proceed: {}", proceed);
            return proceed;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return AjaxResult.builder().code(-1001).message("加锁过程中线程被中断").build();
        } catch (Exception e) {
            log.error("加锁过程中出现异常: {}", e.getMessage());
            return AjaxResult.builder().code(-500).message("系统异常").build();
        } finally {
            if (locked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /*@Around("@annotation(redisLock)")
    public Object aroundRedisLock(ProceedingJoinPoint point, RedisLock redisLock) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        String redisKey = getValBySpEl(redisLock.args(), signature, point.getArgs());
        String value = getValBySpEl(redisLock.value(), signature, point.getArgs());
        String prefix = redisLock.prefix().getValue();
        long timeout = redisLock.timeout();

        String r = prefix + "_" + redisKey;
        JedisUtil util = JedisUtil.build(jedis);
        boolean locked = false;
        try {
            locked = util.lock(r, value, String.valueOf(timeout));
            if (!locked) {
                log.error("加锁失败: {},{}", r, value);
                return AjaxResult.builder().code(200).message("加锁失败: " + r).build();
            }
            Object proceed = point.proceed();
            log.info("proceed: {}", proceed);
            return proceed;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return AjaxResult.builder().code(-1001).message("加锁过程中线程被中断").build();
        } catch (Exception e) {
            log.error("加锁过程中出现异常: {}", e.getMessage());
            return AjaxResult.builder().code(-500).message("系统异常").build();
        } finally {
            if (locked) {
                util.unlock(r, value);
            }
        }
    }*/

    /**
     * 解析spEL表达式
     *
     * @param spEl            spel表达式
     * @param methodSignature methodSignature
     * @param args            args
     * @return string
     */
    private String getValBySpEl(String spEl, MethodSignature methodSignature, Object[] args) {
        //获取方法形参名数组
        String[] paramNames = discoverer.getParameterNames(methodSignature.getMethod());
        if (paramNames != null && paramNames.length > 0) {
            Expression expression = parser.parseExpression(spEl);
            // spring的表达式上下文对象
            EvaluationContext context = new StandardEvaluationContext();
            // 给上下文赋值
            for (int i = 0; i < args.length; i++) {
                //设置变量
                context.setVariable(paramNames[i], args[i]);
            }
            return Objects.requireNonNull(expression.getValue(context)).toString();
        }
        return null;
    }

}
